package com.changgou.item.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.item.dao.SearchDao;
import com.changgou.entity.Page;

import com.changgou.goods.feign.SkuFeign;
import com.changgou.pojo.Sku;

import com.changgou.search.pojo.SkuInfo;

import com.changgou.item.service.SearchService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.*;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 老大哥在看着你
 *
 * @author ANG.YU
 * @date 2019-10-11 14:52
 */
@Service
public  class SearchServiceImpl implements SearchService {
    @Autowired
    private SearchDao searchDao;
    /**查询库存*/
    @Autowired
    private SkuFeign skuFeign;

    @Autowired
    private ElasticsearchTemplate esTemplate;


    @Override
    public void addSpu(String id) {
        List<Sku> skuList = skuFeign.findSkuBySpuId(id);
        List<SkuInfo> skuInfoList = JSON.parseArray(JSON.toJSONString(skuList), SkuInfo.class);
        for (SkuInfo skuInfo : skuInfoList) {
            String spec = skuInfo.getSpec();
            skuInfo.setSpecMap(JSON.parseObject(spec, Map.class));
        }
        searchDao.saveAll(skuInfoList);
    }

    @Override
    public void importData() {
        esTemplate.createIndex(SkuInfo.class);
        esTemplate.putMapping(SkuInfo.class);

        List<Sku> skuList = skuFeign.findSkuListByStatus();
        List<SkuInfo> skuInfoList = JSON.parseArray(JSON.toJSONString(skuList), SkuInfo.class);

        for (SkuInfo skuInfo : skuInfoList) {
            String spec = skuInfo.getSpec();
            skuInfo.setSpecMap(JSON.parseObject(spec, Map.class));
        }
        searchDao.saveAll(skuInfoList);
    }

    @Override
    public Map<String, Object> search(Map<String, String> searchMap) {
        String keywords = searchMap.get("keywords");

        //条件的构建对象
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        //1:关键词 手机 查询结果集即可
        builder.withQuery(QueryBuilders.matchQuery("name",keywords));

        //布尔查询builder
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        if (!StringUtils.isEmpty(searchMap.get("brand"))) {
            //品牌分组统计实现
            String skuBrand = "skuBrand";
            //1:关键词 华为 查询结果集即可
            boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
        }
        Set<Map.Entry<String, String>> entries = searchMap.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            if (entry.getKey().contains("spec_")) {
                boolQueryBuilder.filter(QueryBuilders.termQuery(
                        "specMap." + entry.getKey().substring(5) + ".keyword",
                        entry.getValue().replace("%2B", "+")));
            }
        }

        //08搜索页面之过滤条件价格区间查询
        if (!StringUtils.isEmpty(searchMap.get("price"))) {
            String[] prices = searchMap.get("price").split("-");
            if (prices.length == 2) {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").from(prices[0], true).to(prices[1], false));
            } else {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(prices[0]));
            }

        }

        builder.withFilter(boolQueryBuilder);
        //"@{${url}(sortRule='ASC',sortField='price')}"
        if (!StringUtils.isEmpty(searchMap.get("sortField"))) {
            if ("ASC".equals(searchMap.get("sortRule"))) {
                builder.withSort(SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.ASC));
            } else {
                builder.withSort(SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.DESC));
            }
        }

        //高亮
        HighlightBuilder.Field field = new HighlightBuilder.Field("name")
                .preTags("<font style='color:red'>")
                .postTags("</font>");

        builder.withHighlightFields(field);

        //分页
        String pageNum1 = searchMap.get("pageNum");
        if (null == pageNum1) {
            pageNum1 = "1";
        }
        Integer pageNum = Page.cpn(Integer.parseInt(pageNum1));

        //Es springData 分页

        builder.withPageable(PageRequest.of(pageNum - 1, Page.pageSize));

        //分组查询
        //品牌分组  设置别名  group by brandName
        String skuBrand = "skuBrand";

        builder.addAggregation(AggregationBuilders.terms(skuBrand).field("brandName"));

        //属性分组查询
        //属性分组 设置别名 skuSpec

        String skuSpec = "skuSpec";

        builder.addAggregation(AggregationBuilders.terms(skuSpec).field("spec.keyword"));

        //原生查询esTemplate   SearchDao elasticsearchdata 封装的高级查询下
        AggregatedPage<SkuInfo> page = esTemplate.queryForPage(builder.build(), SkuInfo.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                //List<T> content, Pageable pageable, long total, Aggregations aggregations,String scrollId
                //list 就是查询到的结果集
                List<T> list = new ArrayList<>();

                SearchHits hits = searchResponse.getHits();
                SearchHit[] searchHits = hits.getHits();
               /* System.out.println("searchHits = " + Arrays.toString(searchHits));*/

                for (SearchHit documentFields : searchHits) {
                    SkuInfo skuInfo = JSON.parseObject(documentFields.getSourceAsString(), SkuInfo.class);
                    Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
                    if (!StringUtils.isEmpty(highlightFields)) {
                        skuInfo.setName(highlightFields.get("name").getFragments()[0].toString());
                    }
                    list.add((T)skuInfo);
                }


                return new AggregatedPageImpl<T>(list,pageable, hits.getTotalHits(),searchResponse.getAggregations());
            }
        });

        HashMap resultMap = new HashMap();


        List<SkuInfo> content = page.getContent();
        long total = page.getTotalElements();
        int totalPages = page.getTotalPages();
        String currentpage = searchMap.get("currentpage");
        if (currentpage == null) {
            currentpage = "1";
        }
        Terms brandNameTerms = (Terms) page.getAggregation(skuBrand);
        List<String> brandList = brandNameTerms.getBuckets().stream().map(c -> c.getKeyAsString()).collect(Collectors.toList());

        Terms specNameTerms = (Terms) page.getAggregation(skuSpec);

        List<String> specList = specNameTerms.getBuckets().stream().map(c ->  c.getKeyAsString()).collect(Collectors.toList());

        Map<String, Set<String>> specMap = buildModel(specList);


        resultMap.put("rows", content);
        resultMap.put("keywords", searchMap.get("keywords"));
        resultMap.put("pageNum", pageNum);
        resultMap.put("total", total);
        resultMap.put("totalPages", totalPages);
        resultMap.put("brandList", brandList);
        resultMap.put("specList", specMap);

        return resultMap;
    }

    private Map<String, Set<String>> buildModel(List<String> specList) {
        HashMap<String, Set<String>> result = new HashMap<>();
        for (String spec : specList) {
            Map<String,String> map = JSON.parseObject(spec, Map.class);
            Set<Map.Entry<String, String>> entries = map.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                Set<String> set = result.get(entry.getKey());
                if (null == set) {
                    set = new HashSet<>();
                }
                set.add(entry.getValue());
                result.put(entry.getKey(), set);
            }
        }
        return result;
    }
}
